(function(){Function.prototype.inheritsFrom=function(a){if(a.constructor==Function){this.prototype=new a;this.prototype.constructor=this;this.prototype.parent=a.prototype}else{this.prototype=a;this.prototype.constructor=this;this.prototype.parent=a}return this};
function blur(g){for(var e,a,i,h=g.width,l=g.height,b=g.data,j=2,m,d,c,n,k,f;j--;){if(j){e=h;k=l;c=h<<2}else{e=l;k=h;c=4}for(e=e;e--;){n=j===0?e*h<<2:e<<2;for(i=3;i--;){d=n+i;a=d+c;b[d]=(f=b[d]+b[a]+b[a+c])/3|0;b[a]=(f+=b[a+c*2])>>2|0;b[a+c]=(f+=b[a+c*3])/5|0;for(a=3;a<k-2;a++)b[d+a*c]=(f=(m=f-b[d+(a-2)*c]+b[d+(a+2)*c])>0?m:0)/5|0;b[d+a*c]=(f-=b[d+(a-2)*c])>>2|0;b[d+(a+1)*c]=(f-b[d+(a-1)*c])/3|0}}}return g};
var timer, body=document.body, menuOpened=true, 
Rectangle=function(x, y, w, h, r){
	this.x=x;
	this.y=y;
	this.w=w;
	this.h=h;
	this.r=r||0;
	this.lastx=x;
	this.lasty=y;
};

Rectangle.prototype={
	ctx: null,
	draw: function(color){
		this.ctx.fillStyle=this.ctx.strokeStyle=color;
		
		this.ctx.beginPath();
		this.ctx.moveTo(this.x + this.r, this.y);
		this.ctx.lineTo(this.x + this.w - this.r, this.y);
		this.ctx.quadraticCurveTo(this.x + this.w, this.y, this.x + this.w, this.y + this.r);
		this.ctx.lineTo(this.x + this.w, this.y + this.h - this.r);
		this.ctx.quadraticCurveTo(this.x + this.w, this.y + this.h, this.x + this.w - this.r, this.y + this.h);
		this.ctx.lineTo(this.x + this.r, this.y + this.h);
		this.ctx.quadraticCurveTo(this.x, this.y + this.h, this.x, this.y + this.h - this.r);
		this.ctx.lineTo(this.x, this.y + this.r);
		this.ctx.quadraticCurveTo(this.x, this.y, this.x + this.r, this.y);
		this.ctx.closePath();
		this.ctx.fill();
		if(this.name=='ball'){
			this.ctx.lineWidth=this.r*2;
			this.ctx.lineCap='round';
			this.ctx.beginPath();
			this.ctx.moveTo(this.lastx, this.lasty);
			this.ctx.lineTo(this.lastx=this.x+this.r, this.lasty=this.y+this.r);
			this.ctx.stroke();
		}
	},
	collides:function(rect){
		if(this.solid)
			return this.solid.collides(rect);
        if(this.x + this.w < rect.x)
            return false;
        if(this.y + this.h < rect.y)
            return false;
        if(this.x > rect.x + rect.w)
            return false;
        if(this.y > rect.y + rect.h)
            return false;
        return true;
	}
	
};

var MovingRectangle = function(x, y, w, h, dx, dy, r){
	Rectangle.apply(this, [x, y, w, h, r]);
	this.dx = dx;
	this.dy = dy;
	this.mdx = -1;
	this.mdy = -1;
	this.hx = -1;
};

MovingRectangle.inheritsFrom(Rectangle);
MovingRectangle.prototype.setLimit=function(dx, dy, lx, hx, ly, hy){
	this.mdx = dx;
	this.mdy = dy;
	this.hx = hx;
	this.lx = lx;
	this.hy = hy;
	this.ly = ly;
	this.solid && this.solid.setLimit(dx, dy, lx, hx, ly, hy);
}
MovingRectangle.prototype.accelerate=function(ddx, ddy){
	this.dx += ddx;
	this.dy += ddy;
	this.solid && this.solid.accelerate(ddx, ddy);
	if(Math.abs(this.dx)>this.mdx&&this.mdx != -1){
		if(this.dx > 0)
			this.dx = this.mdx;
		else
			this.dx =- this.mdx;
	}
	if(Math.abs(this.dy) > this.mdy && this.mdy != -1){
		if(this.dy > 0)
			this.dy = this.mdy;
		else
			this.dy =- this.mdy;	
	}
};
MovingRectangle.prototype.deccelerate = function(percent){
	this.dx *= percent/100;
	this.dy *= percent/100;
	this.solid && this.solid.deccelerate(percent);
};
MovingRectangle.prototype.move = function(){
	this.x += this.dx;
	this.y += this.dy;
	this.solid && this.solid.move();
	if(this.hx != -1){
		if(this.x > this.hx-this.w)	
			(this.x = this.hx-this.w) & (this.dx *= -.9);
		else if(this.x < this.lx)		
			(this.x = this.lx) & (this.dx *= -.9);
		if(this.y > this.hy-this.h)	
			(this.y = this.hy-this.h) & (this.dy *= -.9);
		else if(this.y < this.ly)		
			(this.y = this.ly) & (this.dy *= -.9);		
	}
	
};
var Ball = function(x, y){
	if(x > 200)
		var dx = -10;
	else
		var dx = 10;
	if(arguments[2] != undefined)
		dx = 0;
	MovingRectangle.apply(this, [x, y, 10, 10, Math.random()/2.5*dx+dx, 0, 5]);
};
Ball.inheritsFrom(MovingRectangle);
Ball.prototype.name = 'ball'
var Paddle = function(x, y){
	MovingRectangle.apply(this, [x, y, 10, 80, 0, 0, 5]);
	this.solid = new MovingRectangle(x+3.5, y, 7, 80, 0, 0, 0);
	this.setLimit(0, 60, 0, 400, 0, 400);
	this.score = 0;
};
Paddle.inheritsFrom(MovingRectangle);
Paddle.prototype.setSize = function(w, h, sw, sh){
	this.w = w;
	this.h = h;
	this.solid.w = sw;
	this.solid.h = sh;
}
var Score = function(m, phc){
	var score = 0,
		multipler = m,
		hc = phc,				//	handicap
		Phc = Math.abs(hc),		// |handicap|
		hcP = (hc > 0 ? 1 : 0);	// is handicap positive?
	this.update = function(who){
		
		var whoP = who > 0;
		score += (75/(balls.length||1) * //base score
				((whoP) == multipler.positive() ? multipler.get() * //multiplier
				(hcP&&whoP ? hc : 1) * //positive handicap
				((!hcP)&&(!whoP)? Phc : 1) : 1))|0 //negative handicap
	}
	this.get = function(){
		return score
	}
	this.reset = function(){
		score=0;
	}
},
Multiplier = function(){
	var multiplier = 1
	this.increment = function(){
		if(!this.positive())
			this.reset()
		multiplier++
		if(multiplier == 0)
			multiplier++
	}
	this.decrement = function(){
		if(this.positive())
			this.reset()
		multiplier--
		if(multiplier == 0)
			multiplier--
	}
	this.reset = function(){
		multiplier = 1
	}
	this.get = function(){
		return multiplier
	}
	this.positive = function(){
		return multiplier > 0
	}
},
Timer = function(callback, delay) {
    var start, remaining = delay, intervalId, i = 1;
    function finish(){
    	callback();
    	clearInterval(intervalId);
    }
    
    function intervalFunc(){
    	var r = delay-i,
    		m = (r/60)|0,
    		s = r % 60;
    	i++;
    	timeDiv.innerHTML=m+':'+("00"+s).slice(-2);
    	if(i > delay)
    		finish();
    }
	
    this.pause = function() {
        window.clearInterval(intervalId)
        remaining -= new Date() - start
    };

    this.resume = function() {
        start = new Date()
        intervalId = setInterval(intervalFunc, 1000);
    };
	
    this.resume();
}, fadeIn = function(elem){
	for(var i = 0; i < 100; i++)
		setTimeout(function(){ elem.style.opacity = i/100 }, i*10)
}, fadeOut = function(elem){
	for(var i = 100; i > 0; i--)
		setTimeout(function(){ elem.style.opacity = i/100 }, i*10)
},
canvas = document.createElement('canvas'),
					ctx = 
Rectangle.prototype.ctx = 
	 Ball.prototype.ctx = canvas.getContext('2d'),
scoreDiv = document.createElement('h1'),
timeDiv = document.createElement('h2'),
gameDiv = document.createElement('div'), 
pauseDiv = document.createElement('h1'),
menuForm = document.createElement('form'),
instructionButton = document.createElement('input'),
playButton = document.createElement('input'),
ui_setup = function(){ 
	var difButton, difLabel, difficulties, difDiv;
	//canvas
	canvas.height = 
	canvas.width  =400;
	body.appendChild(canvas);
	ctx.fillStyle = 'black';
	ctx.fillRect(0, 0, 400, 400);
	canvas.id = 'canvas';
	
	//ids
	gameDiv.id = 'game';
	menuForm.id = 'menu';
	pauseDiv.id = 'pauseOverlay';
	
	//game data
	scoreDiv.id = 'score';
	gameDiv.appendChild(scoreDiv);
	timeDiv.innerHTML = (TIME/60|0)+':'+ (TIME%60<10? '0' : '') + (TIME%60);
	timeDiv.id = 'time';
	gameDiv.appendChild(timeDiv);
	
	//menu
	instructionButton.type = 
		   playButton.type = 'button'
	instructionButton.id = 'instructionButton';
	instructionButton.value = 'Instructions';
	menuForm.appendChild(instructionButton);
	playButton.id = 'playButton';
	playButton.value = 'Play';
	menuForm.appendChild(playButton);
	
	
	//menu > difficulty
	function makeListenerFunction(d){
		return function(){
			setDifficulty(d);
		}
	}
	difDiv = document.createElement('div')
	difDiv.id = 'difDiv';
	difDiv.innerHTML = 'Chance of ball<br/>spawn each loop'
	menuForm.appendChild(difDiv)
	for(var i in (difficulties = [0.05, 0.1, 0.3, 0.5, 0.7, 0.9])){
		difButton = document.createElement('input');
		difLabel = document.createElement('label');
		difButton.type = 'radio';
		difButton.value = difficulties[i];
		difLabel.innerHTML = difficulties[i]*100+'%'
		if(difficulties[i] == 0.1)
			difButton.checked = true
		difButton.name = 'difficulty';
		difButton.id=difLabel.htmlFor = 'difButton'+difficulties[i];
		difButton.setAttribute('class', 'difButton')
		difLabel .setAttribute('class', 'difLabel' )
		difButton.style.top = 
		difLabel .style.top = (i*30+60)+'px';
		difButton.addEventListener('click', makeListenerFunction(difficulties[i]), false)
		difLabel .addEventListener('click', makeListenerFunction(difficulties[i]), false)
		difDiv.appendChild(difButton);
		difDiv.appendChild(difLabel);
	}
	
	playButton.addEventListener('click', play, false)
	
	body.appendChild(pauseDiv)
	body.appendChild(menuForm)
	body.appendChild(gameDiv)
},
play = function(){
	balls = [];
	scoreObj.reset();
	multiplier.reset();
	clearInterval(timer)
	timer = setInterval(function(){
		update_display();
	}, 80)
	
	fadeOut(menuForm);
	fadeIn(gameDiv);
	
	setTimeout(function(){
		clearInterval(timer)
		timer = setInterval(function(){
			update_display();
			update_positions();
			spawn_balls();
		}, 0)
		Time = new Timer(end, TIME);
	}, 1000);
	
	document.body.addEventListener('keydown', function(e){
		var keycode = e.which;
		switch(keycode){
			case 38:
				keysDown.u = 1;
				break;
			case 40:
				keysDown.d = 1;
				break;
			case 32:
			case 80:
				if(paused){
					fadeOut(pauseDiv);
					setTimeout(function(){
						Time.resume();
						clearInterval(timer);
						timer = setInterval(function(){
							update_positions();
							spawn_balls();
							update_display();
						}, 0);
					}, 1001);
					paused = 0;
				}else{
					Time.pause();
					fadeIn(pauseDiv);
					clearInterval(timer);
					timer = setInterval(function(){
						update_display();
					}, 50);
					paused = 1;
				}
		}
	}, false);
	document.body.addEventListener('keyup', function(e){
		var keycode = e.which;
		switch(keycode){
			case 38:
				keysDown.u = 0;
				break;
			case 40:
				keysDown.d = 0;
				break;
		}
	}, false);
}, 
paused = 0,
keysDown = {
 	u: 0,
 	d: 0
},
replay = function(){
	fadeOut(document.getElementById('finalScore'))
	setTimeout(function(){ body.removeChild(document.getElementById('finalScore')) }, 1001)
	balls = [];
	scoreObj.reset();
	multiplier.reset();
	clearInterval(timer)
	timer = setInterval(function(){
		update_display();
		update_positions();
		spawn_balls();
	}, 0)
	Time = new Timer(end, TIME);
},
end = function(){
	var fsdiv = document.createElement('div'), replayButton = document.createElement('input'), rank, score = scoreObj.get();
//		alert("Your score was: "+(score));
	switch(true){
		case score > 2000:
			rank = 'God'
		case score > 1500:
			rank = 'Godlike'
			break;
		case score > 1000:
			rank = 'Epic'
			break;
		case score > 700:
			rank = 'Good'
			break;
		case score > 500:
			rank = 'Average'
			break;
		case score > 300:
			rank = 'Slight noob'
			break;
		case score > 100:
			rank = 'noob'
			break;
		case score > 0:
			rank = 'Uber noob'
			break;
		default:
			rank = 'Zombie'
			break;
	}
	fsdiv.setAttribute('id', 'finalScore');		
	fsdiv.innerHTML =   "<h1 class='finalScore'>"+score+"</h1>"+ // "<br/>"+
						"<h2 class='finalScore'>"+rank+ "</h2>"; // headers automatically \n
	
	body.appendChild(fsdiv);
	replayButton.addEventListener('click', replay, false);
	replayButton.type = 'button';
	replayButton.value = 'Replay';
	replayButton.id = 'replay';
	fsdiv.appendChild(replayButton);
	fadeIn(document.getElementById('finalScore'));
	clearInterval(timer);
	timer = setInterval(function(){
				update_positions(true);
				spawn_balls();
				update_display();
			}, 50);	
},
paddle = [new Paddle(10, 10), new Paddle(380, 10)],
balls = [],
ps = 80,
ps2 = 40,
bs = 10,
multiplier = new Multiplier(),
scoreObj = new Score(multiplier, .5),
dif = 0.1,
spawn_balls = function(){
	if(Math.random() < dif){
		var i = 0;
		while(balls[i] != undefined && balls[i] != null)
			i++;
		balls[i] = new Ball(Math.random() < .5? 21: 379-bs, Math.random()*(400-bs));
	}
},
setDifficulty = function(d){
	dif = d;
},
update_positions = function(){
	if(arguments[0]){
		var x = 4014,
			y = 200,
			tempx = 10000;
		for(var i = 0; i < balls.length; i++){
			if(balls[i] && balls[i].dx > 0){
				tempx = (400-balls[i].x)/balls[i].dx;
				if(tempx < x){
					x = tempx;
					y = balls[i].y-x*balls[i].dy;
				}
			}
		}
		x = paddle[1].dy > 0? 16: 8;
		if(paddle[1].y+40 > y)
			x =- paddle[1].dy > 0? -16: -8;
		if(Math.abs(paddle[1].y+40-y) < 40)
			paddle[1].deccelerate(25);
		else
			paddle[1].accelerate(0, x);		
	}else{
		if(keysDown.u && !keysDown.d)
			paddle[1].accelerate(0, paddle[1].dy > 0? -16: -8);
		else if(keysDown.d && !keysDown.u)
			paddle[1].accelerate(0, paddle[1].dy < 0? 16: 8);
		else 
			paddle[1].deccelerate(25);
	}
	var x = 4014,
		y = 200,
		tempx = 0;
	for(var i = 0; i < balls.length; i++){
		if(balls[i] && balls[i].dx < 0){
			tempx = balls[i].x/balls[i].dx * -1;
			if(tempx < x){
				x = tempx;
				y = balls[i].y+x*balls[i].dy;
			}
		}
	}
	x = paddle[0].dy > 0? 16: 8;
	if(paddle[0].y+40 > y)
		x = paddle[0].dy < 0? -16: -8;
	if(Math.abs(paddle[0].y+40-y) < 40)
		paddle[0].deccelerate(25);
	else
		paddle[0].accelerate(0, x);
	paddle[1].move();
	paddle[0].move();
	for(i = 0; i < balls.length; i++){
		if(!balls[i])
			continue;
		var j = 0;
		if((balls[i].collides(paddle[0].solid) && balls[i].dx < 0) || (balls[i].collides(paddle[j = 1].solid) && balls[i].dx > 0)){
			balls[i].dx *= -1;
			balls[i].dy += paddle[j].dy>>2;
			balls[i].dy += (balls[i].y-paddle[j].y-40)/80*11;
			if(j == 1)
				multiplier.increment();
		}
		if(balls[i].x <= 0){
			scoreObj.update(1);
			balls[i] = balls[balls.length-1];
			balls.pop();
			continue;
		}
		else if(balls[i].x >= 390){
			scoreObj.update(-1);
			multiplier.decrement();
			balls[i--] = balls[balls.length-1];
			delete balls[balls.length-1];
			continue;
		}
		if(balls[i].y > 390 || balls[i].y <= 0)
			balls[i].dy *= -1;
		balls[i].move()
	}
},
update_display = function(){
	this.hue++;
	if(this.hue >= 360 || !this.hue)
		this.hue = 0;
	ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
	ctx.fillRect(0, 0, 400, 400);
	for(var i = 0; i < balls.length; i++){
		if(!balls[i])
			continue;
		balls[i].draw( 'hsl('+this.hue+', 100%, '+(Math.abs(balls[i].dx)+Math.abs(balls[i].dy))/0.28+25+'%)');
	}
	
	paddle[0].draw('hsl('+this.hue+', 100%, 50%)');
	paddle[1].draw('hsl('+this.hue+', 100%, 50%)');
	scoreDiv.innerHTML = (scoreObj.get()+' x'+multiplier.get());
	ctx.putImageData(blur(ctx.getImageData(0, 0, 400, 400), 1), 0, 0);
}, TIME = 9;

ui_setup();
setDifficulty(0.1)
paddle[0].setLimit(0, 31, 0, 400, 0, 400);
paddle[1].setLimit(0, 31, 0, 400, 0, 400);
paddle[0].setSize(10, 80, 2, 80);
paddle[1].setSize(10, 80, 2, 80);
timer = setInterval(function(){
	update_positions(true);
	spawn_balls();
	update_display();
}, 50);
})();
/*TODOs: 																										Pri			Dif	(1-10)
ADD INSTRUCTIIONS AND <strike>PAUSE INDICATOR</strike>															1			5
USE ".LENGTH" MORE EFFICENTLY (ctrl-f length), for( var i=0, len = balls.length; i<len; i++){ ... } = good		3			2
SONG BASED PLAY																									2			7

Style rules:
	Good:
		if(x)
			doY();
	Bad:
		if(x){												//applys to all block structures
			doY();
		}
		
		if( x )												//applys to all parenthesis
			doY();
			
		if( x ) {doY();}									//applys to all block structures
		
	Good:
		for(var i = 0; i < x.length; i++)
			doY(i);
	Better:
		for(var i = 0, len = x.length; i < len; i++)		//for efficency
			doY(i);
	Bad:
		for(var i=0,len=x.length;i<len;i++)					//applys to all block structures
			doY(i);
	
	Good:
		var x = z > 0? 10*y-2: 10*y+2;
	BAD(!!!!!):
			x = z > 0? 10*y-2: 10*y+2;
	Bad:
		var x = z > 0 ? 10 * y - 2 : 10 * y + 2;
	OK(I guess):
		var x = z > 0 ? 10*y-2 : 10*y+2;
		
	semicolons are nice when remembered, but some places (like the giant var statment that contains most of the functions) need to not have them.
	
	See diff of 13->14 and 14->15 for examples of these.
*/